#include <iostream>
#include <cstring>
#include <tuple>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/CircleShape.hpp>

#include "hdr/game.hpp"

namespace othello
{
	OthelloGame::OthelloGame()
		: m_dir(
			{
				Point(-1, -1),
				Point(0, -1),
				Point(1, -1),
				Point(1, 0),
				Point(1, 1),
				Point(0, 1),
				Point(-1, 1),
				Point(-1, 0),
			})
	{
		initialize();
	}

	void OthelloGame::initialize()
	{
		memset(m_map, 0, sizeof(m_map));
		m_map[3][3] = WHITE;
		m_map[4][4] = WHITE;
		m_map[3][4] = BLACK;
		m_map[4][3] = BLACK;
	}

	void OthelloGame::draw(sf::RenderTarget& target, sf::RenderStates states) const
	{
		target.clear(sf::Color(GP_BACK_COLOR));
		draw_pad(target);
		draw_map(target);
	}

	void OthelloGame::draw_pad(sf::RenderTarget& target)
	{
		// drawing border
		sf::RectangleShape border_line(sf::Vector2f(GP_WIDTH, GP_BORDER_THICK));
		border_line.setFillColor(sf::Color(GP_BODR_COLOR));
		border_line.setPosition(0, 0);

		target.draw(border_line);

		border_line.setPosition(0, GP_HEIGHT - GP_BORDER_THICK);
		target.draw(border_line);

		border_line.rotate(90);
		border_line.setPosition(GP_BORDER_THICK, 0);
		target.draw(border_line);

		border_line.setPosition(GP_WIDTH, 0);
		target.draw(border_line);

		// draw line
		sf::RectangleShape inner_line(sf::Vector2f(GP_WIDTH - GP_BORDER_THICK * 2, GP_LINE_THICK));
		inner_line.setFillColor(sf::Color(GP_LINE_COLOR));

		for (int idx = 1; idx <= 7; idx++)
		{
			inner_line.setPosition(GP_BORDER_THICK, idx * (GP_CELL_HEIGHT + GP_LINE_THICK));
			target.draw(inner_line);
		}

		inner_line.rotate(90);
		for (int idx = 1; idx <= 7; idx++)
		{
			inner_line.setPosition(idx * (GP_CELL_WIDTH + GP_LINE_THICK) + GP_BORDER_THICK, GP_BORDER_THICK);
			target.draw(inner_line);
		}
	}

	void OthelloGame::draw_map(sf::RenderTarget& target) const
	{
		for (int x = 0; x < LINE_CELLS; x++)
			for (int y = 0; y < LINE_CELLS; y++)
			{
				if (m_map[x][y] != 0)
					draw_chess(target, { x, y }, static_cast<Player>(m_map[x][y]));
			}

		/*std::vector<Move> moves;
		const size_t c = get_possible_moves(WHITE, moves);

		if (c > 0)
		{
			sf::RectangleShape r(sf::Vector2f(5.f, 5.f));
			r.setFillColor(sf::Color::Red);
			for (auto& p : moves)
			{
				const Point<float> coord = board_to_screen(p.getPoint());
				r.setPosition(coord.getX(), coord.getY());
				target.draw(r);
			}
		}*/
	}

	Point<float> OthelloGame::board_to_screen(const Point<int>& board_coordinate)
	{
		return {
			static_cast<float>(GP_BORDER_THICK + board_coordinate.get_x() * (GP_CELL_WIDTH + GP_LINE_THICK)),
			static_cast<float>(GP_BORDER_THICK + board_coordinate.get_y() * (GP_CELL_HEIGHT + GP_LINE_THICK))
		};
	}

	Point<int> OthelloGame::screen_to_board(const Point<float>& screen_coordinate)
	{
		return{
			static_cast<int>((screen_coordinate.get_x() - GP_BORDER_THICK) / (GP_CELL_WIDTH + GP_LINE_THICK)),
			static_cast<int>((screen_coordinate.get_y() - GP_BORDER_THICK) / (GP_CELL_HEIGHT + GP_LINE_THICK))
		};
	}


	void OthelloGame::draw_chess(sf::RenderTarget& target, const Point<int>& point, const Player player)
	{
		const int color = player == BLACK ? CHESS_BLACK : CHESS_WHITE;
		sf::CircleShape circle(CHESS_RADIUS);
		circle.setFillColor(sf::Color(color));
		const Point<float> coord = board_to_screen(point);
		const float ax = coord.get_x() + CHESS_MARGIN;
		const float ay = coord.get_y() + CHESS_MARGIN;
		circle.setPosition(ax, ay);
		target.draw(circle);
	}

	size_t OthelloGame::get_possible_moves(const Player player, std::vector<Move>& moves) const
	{
		for (int x = 0; x < LINE_CELLS; x++)
			for (int y = 0; y < LINE_CELLS; y++)
			{
				if (m_map[x][y] != 0)
					continue;

				std::vector<Point<int>> directions;
				for (auto& dir : m_dir)
				{
					int cx = x, cy = y;
					int count = 0;
					bool change_direction = false;
					while (m_map[cx][cy] != static_cast<int>(player))
					{
						if (m_map[cx][cy] == 0 && (cx != x || cy != y))
						{
							change_direction = true;
							break;
						}

						if (m_map[cx][cy] != 0)
							count++;

						cx = cx + dir.get_x();
						cy = cy + dir.get_y();
						if (cx == -1 || cx == LINE_CELLS || cy == -1 || cy == LINE_CELLS)
						{
							change_direction = true;
							break;
						}
					}

					if (change_direction)
						continue;

					if (m_map[cx][cy] == static_cast<int>(player) && count > 0)
					{
						directions.push_back(dir);
						// break;
					}
				}

				if (!directions.empty())
				{
					moves.push_back({{x, y}, directions});
				}
			}

		return moves.size();
	}
}
